package com.peak.spring.boot.tools;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.Date;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.lang3.time.DateUtils;

import com.google.common.collect.Maps;
import com.peak.spring.boot.core.exception.Exceptions;

/**
 * 各种转换的集合
 * 
 * @author gaofeng
 *
 */
public class Converts {

	/**
	 * 转成字符串，日期类型格式化为yyyy-MM-dd HH:mm:ss
	 * 
	 * @param obj
	 * @return
	 */
	public static String toString(Object obj) {
		if (obj == null) {
			return "";
		}
		if (obj instanceof Date) {
			return Dates.format((Date) obj, DatePattern.SHORT_LINE_TIME);
		} else if (obj instanceof Enum) {
			try {
				Method getValueMethod = ((Enum<?>) obj).getClass().getDeclaredMethod("getValue");
				return toString(getValueMethod.invoke(obj));
			} catch (NoSuchMethodException e) {
				return ((Enum<?>) obj).name();
			} catch (Exception e) {
				return "";
			}
		}
		return obj.toString();
	}

	/**
	 * 转成int，取数值
	 * 
	 * @param obj
	 * @return
	 */
	public static int toInt(Object obj) {
		if (obj == null) {
			return 0;
		}
		return Double.valueOf(toString(obj)).intValue();
	}

	/**
	 * 转成Integer
	 * 
	 * @param obj
	 * @return
	 */
	public static Integer toInteger(Object obj) {
		if (obj == null) {
			return null;
		}
		return Double.valueOf(toString(obj)).intValue();
	}

	/**
	 * 转成long
	 * 
	 * @param obj
	 * @return
	 */
	public static long toLongValue(Object obj) {
		if (obj == null) {
			return 0l;
		}
		return Double.valueOf(toString(obj)).longValue();
	}

	/**
	 * 转成Long
	 * 
	 * @param obj
	 * @return
	 */
	public static Long toLong(Object obj) {
		if (obj == null) {
			return null;
		}
		return Double.valueOf(toString(obj)).longValue();
	}

	/**
	 * 转成double
	 * 
	 * @param obj
	 * @return
	 */
	public static double toDoubleValue(Object obj) {
		if (obj == null) {
			return 0.0;
		}
		return Double.parseDouble(toString(obj));
	}

	/**
	 * 转成Double
	 * 
	 * @param obj
	 * @return
	 */
	public static Double toDouble(Object obj) {
		if (obj == null) {
			return null;
		}
		return Double.valueOf(toString(obj));
	}

	/**
	 * 转成float
	 * 
	 * @param obj
	 * @return
	 */
	public static float toFloatValue(Object obj) {
		if (obj == null) {
			return (float) 0.0;
		}
		return Double.valueOf(toString(obj)).floatValue();
	}

	/**
	 * 转成Float
	 * 
	 * @param obj
	 * @return
	 */
	public static Float toFloat(Object obj) {
		if (obj == null) {
			return null;
		}
		return Float.valueOf(toString(obj));
	}

	/**
	 * 转成日期类型，只要满足一种格式就能转换
	 * 
	 * @see DatePattern
	 * 
	 * @param obj
	 * @return
	 */
	public static Date toDate(Object obj) {
		if (obj == null) {
			return null;
		}
		if (obj instanceof Date) {
			return (Date) obj;
		}
		try {
			return DateUtils.parseDate(toString(obj), DatePattern.getParttens());
		} catch (ParseException e) {
			// 转换失败，说明不符合系统要求的格式，具体格式参考DatePattern
			return null;
		}
	}

	/**
	 * 转成boolean
	 * 
	 * @param obj
	 * @return
	 */
	public static boolean toBoolean(Object obj) {
		if (obj == null) {
			return false;
		}
		return Boolean.parseBoolean(toString(obj));
	}

	public static Boolean toBooleanObject(Object obj) {
		return toBoolean(obj) ? Boolean.TRUE : Boolean.FALSE;
	}

	/**
	 * 根据给出的类型进行转换，只支持基本类型，其他类型直接原值返回
	 * 
	 * @param obj
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T toByClass(Object obj, Class<T> clazz) {
		if (String.class == clazz) {
			return (T) toString(obj);
		} else if (Double.class == clazz || double.class == clazz) {
			return (T) toDouble(obj);
		} else if (Integer.class == clazz || int.class == clazz) {
			return (T) toInteger(obj);
		} else if (Float.class == clazz || float.class == clazz) {
			return (T) toFloat(obj);
		} else if (Date.class == clazz) {
			return (T) toDate(obj);
		} else if (Long.class == clazz || long.class == clazz) {
			return (T) toLong(obj);
		} else if (Boolean.class == clazz || boolean.class == clazz) {
			return (T) toBooleanObject(obj);
		} else if (Enum.class.isAssignableFrom(clazz)) {
			if (obj == null) {
				return null;
			}
			@SuppressWarnings("rawtypes")
			Class<? extends Enum> enumClass = (Class<? extends Enum>) clazz;
			return (T) Enum.valueOf(enumClass, String.valueOf(obj));
		} else {
			return (T) obj;
		}
	}
	
	/**
	 * 判断是否基本类型
	 * 
	 * @param clazz
	 * @return
	 */
	public static boolean isSimpleValueType(Class<?> clazz) {
		return (CharSequence.class.isAssignableFrom(clazz) ||
				Number.class.isAssignableFrom(clazz) ||
				Date.class.isAssignableFrom(clazz) ||
				Enum.class.isAssignableFrom(clazz)) ||
				Boolean.class.isAssignableFrom(clazz);
	}

	/**
	 * 只能把基本类型的数据转换成Map中，使用get方法获取value
	 * 
	 * @param javaBean
	 * @return
	 */
	public static Map<String, Object> toMap(Object javaBean) {
		Map<String, Object> map = Maps.newHashMap();
		BeanInfo beanInfo = null;
		try {
			beanInfo = Introspector.getBeanInfo(javaBean.getClass(), Object.class);
			PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
			if (Emptys.isNotEmpty(properties)) {
				for (PropertyDescriptor property : properties) {
					// get read method (getXXX())
					Method readMethod = property.getReadMethod();
					if (readMethod != null) {
						map.put(property.getName(), readMethod.invoke(javaBean));
					}
				}
			}
		} catch (Exception e) {
			throw Exceptions.unchecked(e);
		}
		return map;
	}

	/**
	 * 只能把Map中基本类型的数据赋值到javaBean中，使用set方法赋值
	 * 
	 * @param map
	 * @param javaBeanClass
	 * @return
	 */
	public static <T> T fromMap(Map<String, Object> map, Class<T> javaBeanClass) {
		try {
			return fromMap(map, javaBeanClass.newInstance());
		} catch (Exception e) {
			throw Exceptions.unchecked(e);
		}
	}

	/**
	 * 只能把Map中基本类型的数据赋值到javaBean中，使用set方法赋值
	 * 
	 * @param map
	 * @param javaBean
	 * @return
	 */
	public static <T> T fromMap(Map<String, Object> map, T javaBean) {
		BeanInfo beanInfo = null;
		try {
			@SuppressWarnings("unchecked")
			Class<T> javaBeanClass = (Class<T>) javaBean.getClass();
			beanInfo = Introspector.getBeanInfo(javaBeanClass, Object.class);
			PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
			if (Emptys.isNotEmpty(properties)) {
				for (PropertyDescriptor property : properties) {
					if (!"class".equals(property) && property.getPropertyType() != null) {
						Class<?> propertyType = property.getPropertyType();
						if (isSimpleValueType(propertyType)) {
							// get write method (setXXX())
							Object value = map.get(property.getName());
							// 如果传入的参数包含key，才进行操作
							if (map.containsKey(property.getName())) {
								Method writeMethod = property.getWriteMethod();
								if (value != null) {
									// convert
									value = toByClass(value, propertyType);
									if (writeMethod != null) {
										writeMethod.invoke(javaBean, value);
									}
								} else {
									// 如果value是null，则直接设置为null
									writeMethod.invoke(javaBean, value);
								}
							}
						}
					}
				}
			}
		} catch (Exception e) {
			throw Exceptions.unchecked(e);
		}
		return javaBean;
	}

	/**
	 * 转成url的参数格式
	 * 
	 * @param map
	 * @return
	 */
	public static String toUrlParam(Map<String, String> map) {
		return map.entrySet().stream().map(p -> p.getKey() + "=" + p.getValue()).collect(Collectors.joining("&"));
	}

	/**
	 * 数组中的元素类型转换
	 * 
	 * @param source
	 * @param clazz
	 * @return
	 */
	public static <T> T[] toArrayByClass(Object[] source, Class<T> clazz) {
		if (Emptys.isNotEmpty(source)) {
			@SuppressWarnings("unchecked")
			T[] t = (T[]) Array.newInstance(clazz, source.length);
			for (int i = 0; i < source.length; i++) {
				t[i] = toByClass(source[i], clazz);
			}
			return t;
		}
		return null;
	}
}

